package com.virtualparadigm.compositor;

import java.io.Serializable;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;

import com.virtualparadigm.identity.Identifiable;
import com.virtualparadigm.identity.ResourceIdentifier;

public abstract class CompositePart<CompositorType extends Identifiable> implements Identifiable, Serializable
{
	private static final long serialVersionUID = 1L;
	
	// Why need "wrapped" Compositor instance?
	//   - b/c compositor is implemented via composition, composite part uses inheritance
	private CompositorDelegate<CompositorType> compositorDelegate;
	private ResourceIdentifier resourceIdentifier;
	
	public CompositePart(ResourceIdentifier resourceIdentifier, CompositorDelegate<CompositorType> compositor)
	{
		this.setResourceIdentifier(resourceIdentifier);
		this.setCompositorDelegate(compositor);
	}
	
	public ResourceIdentifier getResourceIdentifier()
	{
		return resourceIdentifier;
	}
	private void setResourceIdentifier(ResourceIdentifier resourceIdentifier)
	{
		this.resourceIdentifier = resourceIdentifier;
	}

	public CompositorType getCompositor()
	{
		CompositorType compositor = null;
		if(this.compositorDelegate != null)
		{
			compositor = this.compositorDelegate.getCompositor();
		}
		return compositor;
	}
	
	public CompositorDelegate<CompositorType> getCompositorDelegate()
	{
		return this.compositorDelegate;
	}
	
	// package protected
	boolean setCompositorDelegate(CompositorDelegate<CompositorType> compositorDelegate)
	{
		if(this.compositorDelegate == null)
		{
			if(compositorDelegate != null)
			{
				this.compositorDelegate = compositorDelegate;
				compositorDelegate.putCompositePart(this);
			}
		}
		else
		{
			if(!this.compositorDelegate.equals(compositorDelegate))
			{
				CompositorDelegate<CompositorType> tempCompositor = this.compositorDelegate;
				this.compositorDelegate = compositorDelegate;
				tempCompositor.removeCompositePart(this.getResourceIdentifier());

				if(compositorDelegate == null)
				{
					this.handleRemovedCompositor();
				}
				else
				{
					compositorDelegate.putCompositePart(this);
				}
			}
		}
		return true;
	}
	

	// if this composite part has other composite parts as children, they should be cleaned up as 
	// well in the implementation of this method
	protected abstract boolean handleRemovedCompositor();	
	
	// ================================================
	// UTILITY METHODS
	// ================================================
	public String toString()
	{
		return ReflectionToStringBuilder.toString(this, ToStringStyleBuilder.toStringStyle);
	}
	
	public int hashCode()
	{
		HashCodeBuilder builder = new HashCodeBuilder();
	    builder.append(this.getResourceIdentifier());
	    return builder.toHashCode();		
	}
	
	public boolean equals(Object obj)
	{
		if (obj == null)
		{
			return false;
		}
		if (obj == this)
		{
			return true;
		}
		if (obj.getClass() != getClass())
		{
			return false;
		}
		CompositePart<CompositorType> that = (CompositePart<CompositorType>)obj;
		EqualsBuilder builder = new EqualsBuilder();
	    builder.append(this.getResourceIdentifier(), that.getResourceIdentifier());
		return builder.isEquals();
	}
	
}